/*
 * Famon (http://code.google.com/p/famon/)
 * Copyright (c) 2010 Carl Crous.
 *
 * This file is part of Famon.
 *
 * Famon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Famon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Famon.  If not, see <http://www.gnu.org/licenses/>.
 */

package za.org.foobar.famon.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.datanucleus.jdo.JDOQuery;

import java.util.*;


/**
 *
 */
public class DBQuery <T> {
    private static final Log log = LogFactory.getLog(DB.class);

    private JDOQuery query;
    private String parameters;
//    private List parameterObjects;
    private Map<String, Object> parameterMap;
    private String where;
    private String orderBy;

    public DBQuery(JDOQuery query) {
        this.query = query;
    }

    public JDOQuery getQuery() {
        return query;
    }

    public Object getResultListAsObject() {
        JDOQuery q = getQuery();
        long t = 0;
        if (log.isDebugEnabled()) {
            t = System.currentTimeMillis();
        }
        Object ret = null;
        try {
            if (parameterMap == null) {
                ret = q.execute();
            } else {
                ret = q.executeWithMap(parameterMap);
            }
            return ret;
        //} catch (JDOFatalException e) {
        } finally {
            if (log.isDebugEnabled()) {
                t = System.currentTimeMillis() - t;
                StringBuilder b = new StringBuilder();
                b.append("Query (").append(t).append("ms):\n\t").append(
                            query.toString()
                                .replace("WHERE", "\n\tWHERE")
                                .replace("PARAMETERS", "\n\tPARAMETERS")
                                .replace("ORDER BY", "\n\tORDER BY"));
                if (parameterMap != null) {
                    b.append("\n\tVALUES ");
                    Set<String> keys = parameterMap.keySet();
                    for (String name : keys) {
                        b.append(name).append(" = \"").append(parameterMap.get(name)).append("\"; ");
                    }
                    if (ret != null && ret instanceof List) {
                        b.append("\n\tRETURNING ").append(((List) ret).size()).append(" results.");
                    }
                }
                log.debug(b.toString());
            }
        }
    }

    public List<T> getResultList() {
        return (List<T>)getResultListAsObject();
    }

    public <V> List<V> getResultList(Class<V> cls) {
        return (List<V>)getResultListAsObject();
    }

    public ArrayList<T> getResultArrayList() {
        return new ArrayList<T>(getResultList());
    }

    public T getSingleResult() {
        List<T> results = getResultList();
        return results.size() > 0 ? results.get(0) : null;
    }

    public DBQuery<T> addWhere(String value) {
        if (where == null) {
            where = "";
        } else {
            where += " && ";
        }
        where += value;
        query.setFilter(where);
        return this;
    }

    public DBQuery<T> addWhere(String field, String paramName, Object value) {
        return addWhere(field + " == " + paramName).addParameter(paramName, value);
    }

    public DBQuery<T> addWhere(String field, Object value) {
        return addWhere(field, field + "Param", value);
    }

    public DBQuery<T> addParameter(String name, Object obj) {
        if (parameters == null) {
            parameters = "";
        } else {
            parameters += ", ";
        }
        if (obj != null) {
            parameters += obj.getClass().getName() + " " + name;
        } else {
            parameters += "java.lang.Object " + name;
        }
//        query.set
        if (parameterMap == null) {
            parameterMap = new TreeMap<String, Object>();
        }
        parameterMap.put(name, obj);
        query.declareParameters(parameters);
        return this;
    }

    public DBQuery<T> setRange(long index, long max) {
        query.setRange(index, max);
        return this;
    }

    public DBQuery<T> addOrderBy(String value) {
        if (orderBy == null) {
            orderBy = "";
        } else {
            orderBy += ", ";
        }
        orderBy += value;
        query.setOrdering(orderBy);
        return this;
    }

    public void delete() {
        JDOQuery q = getQuery();
        if (parameterMap == null) {
            q.deletePersistentAll();
        } else {
            q.deletePersistentAll(parameterMap);
        }
    }
}
